﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;

namespace One.Framework.Util
{
    /// <summary>
    /// 암복호화
    /// </summary>
    public class SecurityHelper
    {
        /// <summary>
        /// Rijndael(AES) 암호화
        /// </summary>
        /// <param name="inputText">문자열</param>
        /// <param name="password">키를 파생시키는데 사용할 암호</param>
        /// <returns></returns>
        public static string EncryptString(string inputText, string password)
        {
            // Rihndael class를 선언하고, 초기화
            RijndaelManaged rijndaelCipher = new RijndaelManaged();

            // 입력받은 문자열을 바이트 배열로 변환
            byte[] PlainText = System.Text.Encoding.Unicode.GetBytes(inputText);

            // 딕셔너리 공격을 대비해서 키를 더 풀기 어렵게 만들기 위해서 
            // Salt를 사용한다.
            byte[] Salt = Encoding.ASCII.GetBytes(password.Length.ToString());

            // PasswordDeriveBytes 클래스를 사용해서 SecretKey를 얻는다.
            PasswordDeriveBytes SecretKey = new PasswordDeriveBytes(password, Salt);

            // Create a encryptor from the existing SecretKey bytes.
            // encryptor 객체를 SecretKey로부터 만든다.
            // Secret Key에는 32바이트
            // (Rijndael의 디폴트인 256bit가 바로 32바이트입니다)를 사용하고, 
            // Initialization Vector로 16바이트
            // (역시 디폴트인 128비트가 바로 16바이트입니다)를 사용한다.
            ICryptoTransform Encryptor = rijndaelCipher.CreateEncryptor(SecretKey.GetBytes(32), SecretKey.GetBytes(16));

            // 메모리스트림 객체를 선언,초기화 
            System.IO.MemoryStream memoryStream = new System.IO.MemoryStream();

            // CryptoStream객체를 암호화된 데이터를 쓰기 위한 용도로 선언
            CryptoStream cryptoStream = new CryptoStream(memoryStream, Encryptor, CryptoStreamMode.Write);

            // 암호화 프로세스가 진행된다.
            cryptoStream.Write(PlainText, 0, PlainText.Length);

            // 암호화 종료
            cryptoStream.FlushFinalBlock();

            // 암호화된 데이터를 바이트 배열로 담는다.
            byte[] CipherBytes = memoryStream.ToArray();

            // 스트림 해제
            memoryStream.Close();
            cryptoStream.Close();

            // 암호화된 데이터를 Base64 인코딩된 문자열로 변환한다.
            string EncryptedData = Convert.ToBase64String(CipherBytes);

            // 최종 결과를 리턴
            return EncryptedData;
        }

        /// <summary>
        /// Rijndael(AES) 복호화
        /// </summary>
        /// <param name="inputText">문자열</param>
        /// <param name="password">키를 파생시키는데 사용할 암호</param>
        /// <returns></returns>
        public static string DecryptString(string inputText, string password)
        {
            RijndaelManaged rijndaelCipher = new RijndaelManaged();

            byte[] EncryptedData = Convert.FromBase64String(inputText);
            byte[] Salt = Encoding.ASCII.GetBytes(password.Length.ToString());

            PasswordDeriveBytes SecretKey = new PasswordDeriveBytes(password, Salt);

            // Decryptor 객체를 만든다.
            ICryptoTransform Decryptor = rijndaelCipher.CreateDecryptor(SecretKey.GetBytes(32), SecretKey.GetBytes(16));

            System.IO.MemoryStream memoryStream = new System.IO.MemoryStream(EncryptedData);

            // 데이터 읽기(복호화이므로) 용도로 cryptoStream객체를 선언, 초기화
            CryptoStream cryptoStream = new CryptoStream(memoryStream, Decryptor, CryptoStreamMode.Read);

            // 복호화된 데이터를 담을 바이트 배열을 선언한다.
            // 길이는 알 수 없지만, 일단 복호화되기 전의 데이터의 길이보다는
            // 길지 않을 것이기 때문에 그 길이로 선언한다.
            byte[] PlainText = new byte[EncryptedData.Length];

            // 복호화 시작
            int DecryptedCount = cryptoStream.Read(PlainText, 0, PlainText.Length);

            memoryStream.Close();
            cryptoStream.Close();

            // 복호화된 데이터를 문자열로 바꾼다.
            string DecryptedData = Encoding.Unicode.GetString(PlainText, 0, DecryptedCount);

            // 최종 결과 리턴
            return DecryptedData;
        }

        /// <summary>
        /// MD5 문자열 암호화
        /// </summary>
        /// <param name="input">The string to encode.</param>
        /// <returns>An encoded string.</returns>
        public static string MD5String(string input)
        {
            // Create a new instance of the 
            // MD5CryptoServiceProvider object.
            MD5 md5Hasher = MD5.Create();

            // Convert the input string to a byte 
            // array and compute the hash.
            byte[] data = md5Hasher.ComputeHash(
               Encoding.Default.GetBytes(input));

            // Create a new Stringbuilder to collect the bytes
            // and create a string.
            StringBuilder sBuilder = new StringBuilder();

            // Loop through each byte of the hashed data 
            // and format each one as a hexadecimal string.
            for (int i = 0; i < data.Length; i++)
            {
                sBuilder.Append(data[i].ToString("x2"));
            }

            // Return the hexadecimal string.
            return sBuilder.ToString();
        }

        /// <summary>
        /// MD5 해시에 대한 문자열을 확인
        /// </summary>
        /// <param name="input">문자열</param>
        /// <param name="hash">해시</param>
        /// <returns>해시 존재 여부</returns>
        public static bool MD5VerifyString(string input, string hash)
        {
            string hashOfInput = MD5String(input);

            StringComparer comparer = StringComparer.OrdinalIgnoreCase;

            if (0 == comparer.Compare(hashOfInput, hash)) { return true; }
            else { return false; }
        }

        /// <summary>
        ///  SHA1 변환
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static string SHA1String(string input)
        {
            // Create a new instance of the 
            // SHA1CryptoServiceProvider object.
            SHA1 sha1Hasher = SHA1.Create();

            // Convert the input string to a byte 
            // array and compute the hash.
            byte[] data = sha1Hasher.ComputeHash(
               Encoding.Default.GetBytes(input));

            // Create a new Stringbuilder to collect the bytes
            // and create a string.
            StringBuilder sBuilder = new StringBuilder();

            // Loop through each byte of the hashed data 
            // and format each one as a hexadecimal string.
            for (int i = 0; i < data.Length; i++)
            {
                sBuilder.Append(data[i].ToString("x2"));
            }

            // Return the hexadecimal string.
            return sBuilder.ToString();
        }

        /// <summary>
        /// SHA1 해시코드 검사
        /// </summary>
        /// <param name="input"></param>
        /// <param name="hash"></param>
        /// <returns></returns>
        public static bool SHA1VerifyString(string input, string hash)
        {
            // Hash the input.
            string hashOfInput = SHA1String(input);

            // Create a StringComparer an comare the hashes.
            StringComparer comparer = StringComparer.OrdinalIgnoreCase;

            if (0 == comparer.Compare(hashOfInput, hash))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }
}
